package com.xd.core.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xd.common.constant.SysConstant;
import com.xd.common.exception.XkdException;
import com.xd.common.properties.PassWordKeyProperties;
import com.xd.common.util.RSAUtils;
import com.xd.common.util.SecurityUtil;
import com.xd.core.company.dto.AddAgencyUserDto;
import com.xd.core.company.service.ICompanyService;
import com.xd.core.system.dto.AddSysUserDto;
import com.xd.core.system.dto.UpdatePasswordDto;
import com.xd.core.system.dto.UpdateSysUserDto;
import com.xd.core.system.dto.UserListDto;
import com.xd.core.system.entity.*;
import com.xd.core.system.mapper.SysUserMapper;
import com.xd.core.system.mapper.SysUserRoleMapper;
import com.xd.core.system.mapper.UserGroupMapper;
import com.xd.core.system.service.IGroupInfoService;
import com.xd.core.system.service.ISysUserService;
import com.xd.core.system.vo.PersonalInfoVo;
import com.xd.core.system.vo.UserDetailVo;
import com.xd.core.system.vo.UserListVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author SJ
 * @since 2023-03-14
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private UserGroupMapper userGroupMapper;
    @Autowired
    private ICompanyService companyService;
    @Autowired
    private IGroupInfoService groupInfoService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private PassWordKeyProperties passWordKeyProperties;
    @Value("${sysRoleIds}")
    private List<Integer> roleIds;
    private static final BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    private static final String redisLogin = "login:";
    private static final String redisToken = "token:";
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addUser(AddSysUserDto addSysUserDto) {
        if (StringUtils.isEmpty(addSysUserDto.getPhone()))
            throw new XkdException("电话不能为空");
        if (!companyService.checkPhoneRepetition(addSysUserDto.getPhone()))
            throw new XkdException("电话号码已注册");
        if (SysConstant.DATA_PERT_TYPE_THREE.equals(addSysUserDto.getDataPertType())) {
            if (StringUtils.isEmpty(addSysUserDto.getDataGroup()))
                throw new XkdException("自定义数据权限不能为空");
        }
        Integer companyId = null;
        String companyType = SecurityUtil.getLoginUser().getUser().getCompanyType();

        if (SysConstant.USER_COMPANY_TYPE_ONE.equals(companyType)) {//平台新建员工
            companyId = SysConstant.SUPER_COMPANY_ID;
            addSysUserDto.setDataPertType(SysConstant.DATA_PERT_TYPE_FOUR);
        } else if (SysConstant.USER_COMPANY_TYPE_FOUR.equals(companyType)) {
            addSysUserDto.setDataPertType(SysConstant.DATA_PERT_TYPE_FOUR);
        } else {
            companyId = SecurityUtil.getLoginCompanyId();
        }
        //插入用户
        SysUser sysUser = new SysUser();
        //校验密码正则规则
        String decrypt = RSAUtils.privateDecrypt(addSysUserDto.getPassword(), passWordKeyProperties.getPrivateKey());
        String rex = "^(?![a-zA-Z]+$)(?!\\d+$)(?![^\\da-zA-Z\\s]+$).{8,16}$";
        if (!Pattern.matches(rex, decrypt))
            throw new XkdException("密码不符合规则");
        BeanUtil.copyProperties(addSysUserDto, sysUser);
        sysUser.setCompanyId(companyId)
                .setUserType(SysConstant.USER_TYPE_B)
                .setCompanyType(companyType)
                .setPassword(bCryptPasswordEncoder.encode(decrypt));
        sysUserMapper.insert(sysUser);
        //插入组
        if (!SysConstant.USER_COMPANY_TYPE_ONE.equals(companyType)) {//非平台
            UserGroup userGroup = new UserGroup();
            userGroup.setUserId(sysUser.getId())
                    .setGroupId(addSysUserDto.getGroupId())
                    .setCompanyId(companyId);
            userGroupMapper.insert(userGroup);
        }
        //插入角色
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setUserId(sysUser.getId())
                .setRoleId(addSysUserDto.getRoleId())
                .setCompanyId(companyId);
        sysUserRoleMapper.insert(sysUserRole);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateUser(UpdateSysUserDto updateSysUserDto) {
        String companyType = SecurityUtil.getLoginUser().getUser().getCompanyType();
        if (StringUtils.isNotEmpty(updateSysUserDto.getPhone())) {
            if (!companyService.checkPhoneRepetition(updateSysUserDto.getPhone()))
                throw new XkdException("电话号码已注册");
        }
        if (SysConstant.DATA_PERT_TYPE_THREE.equals(updateSysUserDto.getDataPertType())) {
            if (StringUtils.isEmpty(updateSysUserDto.getDataGroup()))
                throw new XkdException("自定义数据权限不能为空");
        }
        SysUser sysUser = new SysUser();
        BeanUtil.copyProperties(updateSysUserDto, sysUser);
        /*
         * 校验密码正则规则
         * */
        if (StringUtils.isNotBlank(updateSysUserDto.getPassword())) {
            String decrypt = RSAUtils.privateDecrypt(updateSysUserDto.getPassword(), passWordKeyProperties.getPrivateKey());
            String rex = "^(?![a-zA-Z]+$)(?!\\d+$)(?![^\\da-zA-Z\\s]+$).{8,16}$";
            if (!Pattern.matches(rex, decrypt)) {
                throw new XkdException("密码不符合规则");
            }
            sysUser.setPassword(bCryptPasswordEncoder.encode(decrypt));
        }
        LambdaUpdateWrapper<SysUser> lqw = new LambdaUpdateWrapper<>();
        lqw.eq(SysUser::getId, updateSysUserDto.getId())
                .set(SysUser::getIdCard, updateSysUserDto.getIdCard())
                .set(SysUser::getDataGroup, updateSysUserDto.getDataGroup());
        sysUserMapper.updateById(sysUser);
        /*
         * 修改部门关联
         * */
        if (!SysConstant.USER_COMPANY_TYPE_ONE.equals(companyType)) {
            UserGroup userGroup = new UserGroup();
            userGroup.setGroupId(updateSysUserDto.getGroupId());
            userGroupMapper.update(userGroup, new LambdaQueryWrapper<UserGroup>().eq(UserGroup::getUserId, updateSysUserDto.getId()));
        }

        //如果角色有变化则下线
        SysUserRole oldRole = sysUserRoleMapper.selectOne(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, updateSysUserDto.getId()));
        if (!Objects.equals(oldRole.getRoleId(), updateSysUserDto.getRoleId())) {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setRoleId(updateSysUserDto.getRoleId());
            sysUserRoleMapper.update(sysUserRole, new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, updateSysUserDto.getId()));
            redisTemplate.delete(redisLogin + updateSysUserDto.getId());
            redisTemplate.delete(redisToken + updateSysUserDto.getId());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteUser(Integer id) {
        sysUserMapper.deleteById(id);
        //下线
        redisTemplate.delete(redisLogin + id);
        redisTemplate.delete(redisToken + id);
    }

    @Override
    public Page<UserListVo> userList(UserListDto userListDto) {
        userListDto.setUserId(SecurityUtil.getLoginUserId());
        Integer companyId = SecurityUtil.getLoginCompanyId();
        PageHelper.startPage(userListDto.getPageNum(), userListDto.getPageSize());
        return (Page<UserListVo>) sysUserMapper.userList(userListDto, companyId, SysConstant.USER_TYPE_B);
    }

    /*
     * 获取数据权限的用户ID
     * 能够看到的数据用户ID集合
     * */
    @Override
    public List<Integer> getUserIdsByGroup() {
        SysUser user = SecurityUtil.getLoginUser().getUser();
        Integer companyId = SecurityUtil.getLoginCompanyId();
        List<Integer> userIds = new ArrayList<>();
        if (SecurityUtil.getLoginUser().getUser().getUserType().equals(SysConstant.USER_TYPE_P) || user.getDataPertType().equals(SysConstant.DATA_PERT_TYPE_FOUR))
            return userIds;
        List<Integer> groupIds = new ArrayList<>();
        switch (user.getDataPertType()) {
            case SysConstant.DATA_PERT_TYPE_ONE:
                userIds.add(user.getId());
                return userIds;
            //根据数据权限展示用户列表
            case SysConstant.DATA_PERT_TYPE_TOW:
                List<GroupInfo> groupInfos = groupInfoService.list(new LambdaQueryWrapper<GroupInfo>().eq(GroupInfo::getCompanyId, companyId));
                Integer groupId = SecurityUtil.getGroupId();
                groupIds.add(groupId);
                groupInfoService.getChildIds(groupId, groupIds, groupInfos);
                userIds = userGroupMapper.getUserIdsByGroupIds(groupIds);
                break;
            case SysConstant.DATA_PERT_TYPE_THREE:
                String dataGroup = user.getDataGroup();
                groupIds = Arrays.stream(dataGroup.split(",")).map(Integer::valueOf).collect(Collectors.toList());
                userIds = userGroupMapper.getUserIdsByGroupIds(groupIds);
                userIds.add(user.getId());
                break;
            case SysConstant.DATA_PERT_TYPE_FOUR:
                List<SysUser> sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>().eq(SysUser::getCompanyId, companyId));
                userIds = sysUsers.stream().map(SysUser::getId).collect(Collectors.toList());
                break;
        }

        return userIds;
    }

    @Override
    public UserDetailVo userDetail(Integer id) {
        return sysUserMapper.userDetail(id);
    }

    @Override
    public List<SysRole> roleListDropDown() {
        Integer loginCompanyId = SecurityUtil.getLoginCompanyId();
        return sysUserMapper.roleListDropDown(loginCompanyId, roleIds);
    }

    @Override
    public void updatePassword(UpdatePasswordDto dto) {
        String companyType = SecurityUtil.getLoginUser().getUser().getCompanyType();
        if (companyType.equals(SysConstant.USER_COMPANY_TYPE_ONE) || companyType.equals(SysConstant.USER_COMPANY_TYPE_TOW)) {//不是平台,代理
            if (StringUtils.isNotEmpty(dto.getPassword())) {
                //校验密码正则规则
                String decrypt = RSAUtils.privateDecrypt(dto.getPassword(), passWordKeyProperties.getPrivateKey());
                String rex = "^(?![a-zA-Z]+$)(?!\\d+$)(?![^\\da-zA-Z\\s]+$).{8,16}$";
                if (!Pattern.matches(rex, decrypt)) {
                    throw new XkdException("密码不符合规则");
                }
                AddAgencyUserDto addAgencyUserDto = BeanUtil.copyProperties(dto, AddAgencyUserDto.class);
                String password = bCryptPasswordEncoder.encode(decrypt);
                addAgencyUserDto.setPassword(password);
                SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhone, dto.getPhone()));
                boolean matches = bCryptPasswordEncoder.matches(password, sysUser.getPassword());
                if (matches) {
                    throw new XkdException("新密码不能与旧密码相同");
                }
                Integer id = sysUser.getId();
                sysUserMapper.updatePassword(addAgencyUserDto, id);
                redisTemplate.delete(redisLogin + id);
                redisTemplate.delete(redisToken + id);
            }
        }else{
            throw new XkdException("非平台用户不能操作");
        }
    }

    /**
     * @description: 获取个人资料
     * @author: ZCH
     * @date: 2025/4/2 0002 20:08
     * @param: [userId]
     * @return: com.xd.core.system.vo.PersonalInfoVo
     **/
    @Override
    public PersonalInfoVo getPersonalInfo(Integer userId) {
        if (StrUtil.isEmpty(userId.toString()))
            throw new XkdException("参数错误");
        PersonalInfoVo personalInfoVo = sysUserMapper.getPersonalInfo(userId);
        return personalInfoVo;
    }

    /*
     * 获取数据权限所有拥有的部门ID集合
     * */
    @Override
    public List<Integer> getGroupIds() {
        SysUser user = SecurityUtil.getLoginUser().getUser();
        Integer companyId = SecurityUtil.getLoginCompanyId();
        Integer groupId = SecurityUtil.getGroupId();
        List<Integer> groupIds = new ArrayList<>();
//        if (SecurityUtil.getLoginUser().getUser().getUserType().equals(SysConstant.USER_TYPE_P) || user.getDataPertType().equals(SysConstant.DATA_PERT_TYPE_FOUR))
//            return groupIds;
        switch (user.getDataPertType()) {
            case SysConstant.DATA_PERT_TYPE_ONE:
                groupIds.add(groupId);
                break;
            //根据数据权限展示用户列表
            case SysConstant.DATA_PERT_TYPE_TOW:
                List<GroupInfo> groupInfos = groupInfoService.list(new LambdaQueryWrapper<GroupInfo>().eq(GroupInfo::getCompanyId, companyId));
                groupIds.add(groupId);
                groupInfoService.getChildIds(groupId, groupIds, groupInfos);
                break;
            case SysConstant.DATA_PERT_TYPE_THREE:
                String dataGroup = user.getDataGroup();
                groupIds = Arrays.stream(dataGroup.split(",")).map(Integer::valueOf).collect(Collectors.toList());
                break;
            case SysConstant.DATA_PERT_TYPE_FOUR:
                List<GroupInfo> groups = groupInfoService.list(new LambdaQueryWrapper<GroupInfo>().eq(GroupInfo::getCompanyId, companyId));
                groupIds = groups.stream().map(GroupInfo::getId).collect(Collectors.toList());
                break;
        }

        return groupIds;
    }
}
